#include "entity.h"
#include "camera.h"
#include "light.h"
#include "scene.h"
#include "common.h"

#include "helpers/helper.h"

namespace Opl{ namespace Gui {



		const qreal ROTATE_SPEED_X = 30.0 / 1000.0;
		const qreal ROTATE_SPEED_Y = 20.0 / 1000.0;
		const qreal ROTATE_SPEED_Z = 40.0 / 1000.0;
		const int MAX_ITEM_SIZE = 512;
		const int MIN_ITEM_SIZE = 16;

		class EntityPrivate{
		public:
			EntityPrivate():
					m_isResizing(false)
			{}
			~EntityPrivate()
			{}

            int m_size;
            QTime m_startTime;
            bool m_isResizing;
		};

		Entity::Entity(int size, int x, int y) :
				O_PRIVATE_CREATE(Entity)
		{
			O_D(Entity);
			d->m_size = size;
			setFlag(QGraphicsItem::ItemIsMovable, true);
			setFlag(QGraphicsItem::ItemIsSelectable, true);
			setFlag(QGraphicsItem::ItemIsFocusable, true);
			setAcceptHoverEvents(true);
			setPos(x, y);
			d->m_startTime = QTime::currentTime();
		}

		Entity::~Entity()
		{
			O_PRIVATE_DELETE;
		}

		QTime& Entity::startTime()
		{
			O_D(Entity);
			return d->m_startTime;
		}

		QRectF Entity::boundingRect() const
		{
			O_C(Entity);
			return QRectF(-d->m_size / 2, -d->m_size / 2, d->m_size, d->m_size);
		}

		void Entity::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *)
		{
			O_D(Entity);
			if (option->state & QStyle::State_Selected) {
				painter->setRenderHint(QPainter::Antialiasing, true);
				if (option->state & QStyle::State_HasFocus)
					painter->setPen(Qt::yellow);
				else
					painter->setPen(Qt::white);
				painter->drawRect(boundingRect());

				painter->drawLine(d->m_size / 2 - 9, d->m_size / 2, d->m_size / 2, d->m_size / 2 - 9);
				painter->drawLine(d->m_size / 2 - 6, d->m_size / 2, d->m_size / 2, d->m_size / 2 - 6);
				painter->drawLine(d->m_size / 2 - 3, d->m_size / 2, d->m_size / 2, d->m_size / 2 - 3);

				painter->setRenderHint(QPainter::Antialiasing, false);
			}
		}

		void Entity::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
		{
			O_D(Entity);
			if (!isSelected() && scene()) {
				scene()->clearSelection();
				setSelected(true);
			}

			QMenu menu;
			QAction *delAction = menu.addAction("Delete");
			QAction *newAction = menu.addAction("New");
			QAction *growAction = menu.addAction("Grow");
			QAction *shrinkAction = menu.addAction("Shrink");

			QAction *selectedAction = menu.exec(event->screenPos());

//			if (selectedAction == delAction)
//				deleteSelectedItems(scene());
//			else if (selectedAction == newAction)
//				duplicateSelectedItems(scene());
//			else if (selectedAction == growAction)
//				growSelectedItems(scene());
//			else if (selectedAction == shrinkAction)
//				shrinkSelectedItems(scene());
		}

//		void Entity::duplicateSelectedItems(QGraphicsScene *scene)
//		{
//			if (!scene)
//				return;

//			O_D(Entity);
//			QList<QGraphicsItem *> selected;
//			selected = scene->selectedItems();

//			foreach (QGraphicsItem *item, selected) {
//				Entity *entity = qgraphicsitem_cast<Entity *>(item);
//				if (entity)
//					scene->addItem(entity->createNew(entity->m_size, Entity->pos().x() + Entity->m_size, Entity->pos().y()));
//			}
//		}

		void Entity::deleteSelectedItems(QGraphicsScene *scene)
		{
			if (!scene)
				return;


			QList<QGraphicsItem *> selected;
			selected = scene->selectedItems();

			foreach (QGraphicsItem *item, selected) {
				Entity *entity = qgraphicsitem_cast<Entity *>(item);
				if (entity)
					delete entity;
			}
		}

//		void Entity::growSelectedItems(QGraphicsScene *scene)
//		{
//			if (!scene)
//				return;

//			O_D(Entity);
//			QList<QGraphicsItem *> selected;
//			selected = scene->selectedItems();

//			foreach (QGraphicsItem *item, selected) {
//				Entity *Entity = qgraphicsitem_cast<Entity *>(item);
//				if (Entity) {
//					Entity->prepareGeometryChange();
//					Entity->m_size *= 2;
//					if (Entity->m_size > MAX_ITEM_SIZE)
//						Entity->m_size = MAX_ITEM_SIZE;
//				}
//			}
//		}

//		void Entity::shrinkSelectedItems(QGraphicsScene *scene)
//		{
//			if (!scene)
//				return;

//			O_D(Entity);
//			QList<QGraphicsItem *> selected;
//			selected = scene->selectedItems();

//			foreach (QGraphicsItem *item, selected) {
//				Entity *entity = qgraphicsitem_cast<Entity *>(item);
//				if (entity) {
//					entity->prepareGeometryChange();
//					entity->m_size /= 2;
//					if (entity->m_size < MIN_ITEM_SIZE)
//						entity->m_size = MIN_ITEM_SIZE;
//				}
//			}
//		}

		void Entity::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
		{
			O_D(Entity);
			if (d->m_isResizing) {
				int dx = int(2.0 * event->pos().x());
				int dy = int(2.0 * event->pos().y());
				prepareGeometryChange();
				d->m_size = (dx > dy ? dx : dy);
				if (d->m_size < MIN_ITEM_SIZE)
					d->m_size = MIN_ITEM_SIZE;
				else if (d->m_size > MAX_ITEM_SIZE)
					d->m_size = MAX_ITEM_SIZE;
			} else {
				QGraphicsItem::mouseMoveEvent(event);
			}
		}

		void Entity::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
		{
			O_D(Entity);
			if (d->m_isResizing || (isInResizeArea(event->pos()) && isSelected()))
				setCursor(Qt::SizeFDiagCursor);
			else
				setCursor(Qt::ArrowCursor);
			QGraphicsItem::hoverMoveEvent(event);
		}

		void Entity::mousePressEvent(QGraphicsSceneMouseEvent *event)
		{
			O_D(Entity);
			static qreal z = 0.0;
			setZValue(z += 1.0);
			if (event->button() == Qt::LeftButton && isInResizeArea(event->pos())) {
				d->m_isResizing = true;
			} else {
				QGraphicsItem::mousePressEvent(event);
			}
		}

		void Entity::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
		{
			O_D(Entity);
			if (event->button() == Qt::LeftButton && d->m_isResizing) {
				d->m_isResizing = false;
			} else {
				QGraphicsItem::mouseReleaseEvent(event);
			}
		}

		void Entity::keyPressEvent(QKeyEvent *event)
		{
			O_D(Entity);
			switch (event->key()) {
			case Qt::Key_Delete:
				deleteSelectedItems(scene());
				break;
//			case Qt::Key_Insert:
//				duplicateSelectedItems(scene());
//				break;
//			case Qt::Key_Plus:
//				growSelectedItems(scene());
//				break;
//			case Qt::Key_Minus:
//				shrinkSelectedItems(scene());
//				break;
			default:
				QGraphicsItem::keyPressEvent(event);
				break;
			}
		}

		void Entity::wheelEvent(QGraphicsSceneWheelEvent *event)
		{
			O_D(Entity);
			prepareGeometryChange();
			d->m_size = int(d->m_size * exp(-event->delta() / 600.0));
			if (d->m_size > MAX_ITEM_SIZE)
				d->m_size = MAX_ITEM_SIZE;
			else if (d->m_size < MIN_ITEM_SIZE)
				d->m_size = MIN_ITEM_SIZE;
		}

		int Entity::type() const
		{
			O_C(Entity);
			return Type;
		}


		bool Entity::isInResizeArea(const QPointF &pos)
		{
			O_D(Entity);
			return (-pos.y() < pos.x() - d->m_size + 9);
		}





















		QtBox::QtBox(int size, int x, int y) : Entity(size, x, y), m_texture(0)
		{

			for (int i = 0; i < 8; ++i) {
				m_vertices[i].setX(i & 1 ? 0.5f : -0.5f);
				m_vertices[i].setY(i & 2 ? 0.5f : -0.5f);
				m_vertices[i].setZ(i & 4 ? 0.5f : -0.5f);
			}
			for (int i = 0; i < 4; ++i) {
				m_texCoords[i].setX(i & 1 ? 1.0f : 0.0f);
				m_texCoords[i].setY(i & 2 ? 1.0f : 0.0f);
			}
			m_normals[0] = QVector3D(-1.0f, 0.0f, 0.0f);
			m_normals[1] = QVector3D(1.0f, 0.0f, 0.0f);
			m_normals[2] = QVector3D(0.0f, -1.0f, 0.0f);
			m_normals[3] = QVector3D(0.0f, 1.0f, 0.0f);
			m_normals[4] = QVector3D(0.0f, 0.0f, -1.0f);
			m_normals[5] = QVector3D(0.0f, 0.0f, 1.0f);
		}

		QtBox::~QtBox()
		{
			if (m_texture)
				delete m_texture;
		}

		Entity *QtBox::createNew(int size, int x, int y)
		{
			return new QtBox(size, x, y);
		}

		void QtBox::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
		{
			QRectF rect = boundingRect().translated(pos());
			float width = float(painter->device()->width());
			float height = float(painter->device()->height());

			float left = 2.0f * float(rect.left()) / width - 1.0f;
			float right = 2.0f * float(rect.right()) / width - 1.0f;
			float top = 1.0f - 2.0f * float(rect.top()) / height;
			float bottom = 1.0f - 2.0f * float(rect.bottom()) / height;
			float moveToRectMatrix[] = {
				0.5f * (right - left), 0.0f, 0.0f, 0.0f,
				0.0f, 0.5f * (bottom - top), 0.0f, 0.0f,
				0.0f, 0.0f, 1.0f, 0.0f,
				0.5f * (right + left), 0.5f * (bottom + top), 0.0f, 1.0f
			};

			painter->beginNativePainting();

			glMatrixMode(GL_PROJECTION);
			glPushMatrix();
			glLoadMatrixf(moveToRectMatrix);
			gluPerspective(60.0, 1.0, 0.01, 10.0);

			glMatrixMode(GL_MODELVIEW);
			glPushMatrix();
			glLoadIdentity();

			//glEnable(GL_DEPTH_TEST);
			glEnable(GL_CULL_FACE);
			glEnable(GL_LIGHTING);
			glEnable(GL_COLOR_MATERIAL);
			glEnable(GL_NORMALIZE);

			if(m_texture == 0)
				m_texture = new Helpers::OpenGL::GLTexture2D(":/res/boxes/qt-logo.jpg", 64, 64);
			m_texture->bind();
			glEnable(GL_TEXTURE_2D);

			glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
			float lightColour[] = {1.0f, 1.0f, 1.0f, 1.0f};
			float lightDir[] = {0.0f, 0.0f, 1.0f, 0.0f};
			glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColour);
			glLightfv(GL_LIGHT0, GL_POSITION, lightDir);
			glEnable(GL_LIGHT0);

			glTranslatef(0.0f, 0.0f, -1.5f);
			glRotatef(ROTATE_SPEED_X * startTime().msecsTo(QTime::currentTime()), 1.0f, 0.0f, 0.0f);
			glRotatef(ROTATE_SPEED_Y * startTime().msecsTo(QTime::currentTime()), 0.0f, 1.0f, 0.0f);
			glRotatef(ROTATE_SPEED_Z * startTime().msecsTo(QTime::currentTime()), 0.0f, 0.0f, 1.0f);
			int dt = startTime().msecsTo(QTime::currentTime());
			if (dt < 500)
				glScalef(dt / 500.0f, dt / 500.0f, dt / 500.0f);

			for (int dir = 0; dir < 3; ++dir) {
				glColor4f(1.0f, 1.0f, 1.0f, 1.0);

				glBegin(GL_TRIANGLE_STRIP);
				glNormal3fv(reinterpret_cast<float *>(&m_normals[2 * dir + 0]));
				for (int i = 0; i < 2; ++i) {
					for (int j = 0; j < 2; ++j) {
						glTexCoord2fv(reinterpret_cast<float *>(&m_texCoords[(j << 1) | i]));
						glVertex3fv(reinterpret_cast<float *>(&m_vertices[(i << ((dir + 2) % 3)) | (j << ((dir + 1) % 3))]));
					}
				}
				glEnd();

				glBegin(GL_TRIANGLE_STRIP);
				glNormal3fv(reinterpret_cast<float *>(&m_normals[2 * dir + 1]));
				for (int i = 0; i < 2; ++i) {
					for (int j = 0; j < 2; ++j) {
						glTexCoord2fv(reinterpret_cast<float *>(&m_texCoords[(j << 1) | i]));
						glVertex3fv(reinterpret_cast<float *>(&m_vertices[(1 << dir) | (i << ((dir + 1) % 3)) | (j << ((dir + 2) % 3))]));
					}
				}
				glEnd();
			}
			m_texture->unbind();

			//glDisable(GL_DEPTH_TEST);
			glDisable(GL_CULL_FACE);
			glDisable(GL_LIGHTING);
			glDisable(GL_COLOR_MATERIAL);
			glDisable(GL_TEXTURE_2D);
			glDisable(GL_LIGHT0);
			glDisable(GL_NORMALIZE);

			glPopMatrix();

			glMatrixMode(GL_PROJECTION);
			glPopMatrix();

			painter->endNativePainting();

			Entity::paint(painter, option, widget);
		}

	};};
